using System;
using System.Diagnostics;
using System.IO;
using Bitcoin.Diagnostics;
using Bitcoin.IO;
using Org.BouncyCastle.Utilities.Encoders;

namespace Bitcoin
{
    /// <summary>
    ///     A TransactionOutput message contains a scriptPubKey that controls who is able to spend its value. It is a sub-part
    ///     of the Transaction message.
    /// </summary>
    //[Serializable]
    public class TransactionOutput
    {
        private ILog _log = LogService.GetLog(typeof(TransactionOutput));
        private bool _availableForSpending;

        // A transaction output has some value and a script used for authenticating that the redeemer is allowed to spend
        // this output.
        private byte[] _scriptBytes;

        // The script bytes are parsed and turned into a Script on demand.
        /*[NonSerialized]*/
        private Script _scriptPubKey;

        // These fields are Java serialized but not BitCoin serialized. They are used for tracking purposes in our wallet
        // only. If set to true, this output is counted towards our balance. If false and spentBy is null the tx output
        // was owned by us and was sent to somebody else. If false and spentBy is true it means this output was owned by
        // us and used in one of our own transactions (eg, because it is a change output).
        private TransactionInput _spentBy;
        private ulong _value;

        public TransactionOutput(ulong value, byte[] scriptBytes)
        {
            _value = value;
            _scriptBytes = scriptBytes;
            _availableForSpending = true;
        }

        /// <summary>
        ///     Used only in creation of the genesis blocks and in unit tests.
        /// </summary>
        internal TransactionOutput(ChainParameters parameters, Transaction parent, byte[] scriptBytes)
        {
            _scriptBytes = scriptBytes;
            _value = Temp_BitcoinUtility.ToSatoshis(50, 0);
            ParentTransaction = parent;
            _availableForSpending = true;
        }

        public TransactionOutput(ChainParameters parameters, Transaction parent, ulong value, BitcoinAddress to)
        {
            _value = value;
            _scriptBytes = Script.CreateOutputScript(to);
            ParentTransaction = parent;
            _availableForSpending = true;
        }

        public bool IsAvailableForSpending
        {
            get { return _availableForSpending; }
        }

        public ILog Log
        {
            get { return _log; }
            set { _log = value; }
        }

        // A reference to the transaction which holds this output.
        public Transaction ParentTransaction { get; set; }

        public byte[] ScriptBytes
        {
            get { return _scriptBytes; }
        }

        /// <exception cref="ScriptException" />
        public Script ScriptPubKey
        {
            get { return _scriptPubKey ?? (_scriptPubKey = new Script(null, _scriptBytes, 0, _scriptBytes.Length)); }
        }

        /// <summary>
        ///     Returns the connected input.
        /// </summary>
        public TransactionInput SpentBy
        {
            get { return _spentBy; }
        }

        /// <summary>
        ///     Returns the value of this output in Satoshis. This is the amount of currency that the destination address
        ///     receives.
        /// </summary>
        public ulong Value
        {
            get { return _value; }
        }

        /// <summary>
        ///     Sets this objects availableToSpend flag to false and the spentBy pointer to the given input.
        ///     If the input is null, it means this output was signed over to somebody else rather than one of our own keys.
        /// </summary>
        public void MarkAsSpent(TransactionInput input)
        {
            Debug.Assert(_availableForSpending);
            _availableForSpending = false;
            _spentBy = input;
        }

        /// <summary>
        ///     Returns a human readable debug string.
        /// </summary>
        public override string ToString()
        {
            var toHash = ScriptPubKey.PubKeyHash;
            return "TxOut of " + Temp_BitcoinUtility.BitcoinValueToFriendlyString(_value) + " to " +
                   Hex.ToHexString(toHash) +
                   " script:" + ScriptPubKey;
        }

        internal int Index
        {
            get
            {
                Debug.Assert(ParentTransaction != null);
                for (var i = 0; i < ParentTransaction.Outputs.Count; i++)
                {
                    if (ParentTransaction.Outputs[i] == this)
                        return i;
                }
                // Should never happen.
                throw new Exception("Output linked to wrong parent transaction?");
            }
        }

        internal void MarkAsUnspent()
        {
            _availableForSpending = true;
            _spentBy = null;
        }
    }
}